home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 5 / MacMania 5.toast / / Internet software / NewsWatcher / NW Source / Source / search.c < prev    next >
Text File  |  1997-01-09  |  9KB  |  360 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     search.c
  4.  
  5.     This module handles the "Search" command.
  6.     
  7.     Copyright © 1994-1997, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include <stdio.h>
  14.  
  15. #include "glob.h"
  16. #include "search.h"
  17. #include "dialog.h"
  18. #include "newswatcher.h"
  19. #include "mark.h"
  20. #include "menus.h"
  21. #include "news.h"
  22. #include "group.h"
  23. #include "popuputil.h"
  24. #include "subscribe.h"
  25. #include "status.h"
  26. #include "memutil.h"
  27. #include "strutil.h"
  28. #include "header.h"
  29. #include "full.h"
  30. #include "biglist.h"
  31.  
  32.  
  33.  
  34. #define kSearchDlg            140            /* Search dialog */
  35. #define kSearchHeader        3
  36. #define    kSearchPattern        4
  37. #define kSearchPopup        5
  38.  
  39.  
  40.  
  41. /*----------------------------------------------------------------------------
  42.     DoSearchDialog
  43.     
  44.     Present the search dialog.
  45.             
  46.     Exit:    function result = error code.
  47.             header = header name.
  48.             pattern = search string.
  49. ----------------------------------------------------------------------------*/
  50.  
  51. static OSErr DoSearchDialog (CStr255 header, CStr255 pattern)
  52. {
  53.     static CStr255 headerSave = "";
  54.     static CStr255 patternSave = "";
  55.     DialogPtr dlg = nil;
  56.     short item;
  57.     OSErr err = noErr;
  58.     
  59.     if (*headerSave == 0) GetCString(kStrDefaultSearchHdr, headerSave);
  60.     
  61.     err = MyGetNewDialog(kSearchDlg, ok, cancel, &dlg);
  62.     if (err != noErr) return err;
  63.     RestoreMovableModalDialogPosition(dlg, gPrefs.searchLoc);
  64.     
  65.     strcpy(header, headerSave);
  66.     strcpy(pattern, patternSave);
  67.  
  68.     DlgSetCString(dlg, kSearchHeader, header);
  69.     SetItemKeyword(dlg, kSearchHeader);
  70.     SetItemMaxLength(dlg, kSearchHeader, 255);
  71.     DlgSetCString(dlg, kSearchPattern, pattern);
  72.     SetItemMaxLength(dlg, kSearchPattern, 255);
  73.     SelectDialogItemText(dlg, kSearchPattern, 0, 255);
  74.     SetItemPopupTypeinItem(dlg, kSearchPopup, kSearchHeader);
  75.     
  76.     do {
  77.         DlgEnableItem(dlg, ok, *header != 0 && *pattern != 0);
  78.         MyMovableModalDialog(dlg, DialogFilter, &item);
  79.         switch (item) {
  80.             case kSearchPopup:
  81.             case kSearchHeader:
  82.                 DlgGetCString(dlg, kSearchHeader, header);
  83.                 break;
  84.             case kSearchPattern:
  85.                 DlgGetCString(dlg, kSearchPattern, pattern);
  86.                 break;
  87.         }
  88.     } while (item != ok && item != cancel);
  89.     
  90.     SaveMovableModalDialogPosition(dlg, &gPrefs.searchLoc);
  91.     err = DoClose(dlg);
  92.     if (err != noErr) return err;
  93.     
  94.     if (item == cancel) return userCanceledErr;
  95.     
  96.     strcpy(headerSave, header);
  97.     strcpy(patternSave, pattern);
  98.     return noErr;
  99. }
  100.  
  101.  
  102.  
  103. /*----------------------------------------------------------------------------
  104.     SearchOneGroup 
  105.     
  106.     Search a single group.
  107.     
  108.     Entry:    header = name of the header to be searched.
  109.             pattern = search string.
  110.             *theGroup = group record.
  111.             
  112.     Exit:    function result = error code.
  113.             theGroup->numUnread = number of matched articles.
  114.             theGroup->unread = handle to unread list.
  115. ----------------------------------------------------------------------------*/
  116.  
  117. static OSErr SearchOneGroup (char *header, char *pattern, TGroup *theGroup)
  118. {
  119.     CStr255    groupName, statusStr;
  120.     THeader **headers = nil;
  121.     short numHeaders;
  122.     THeader *pHeader, *pHeaderEnd;
  123.     long firstUnread, lastUnread;
  124.     long number;
  125.     OSErr err = noErr;
  126.     Boolean groupExists;
  127.  
  128.     strcpy(groupName, *gGroupNames + theGroup->nameOffset);
  129.  
  130.     GetCString(kStrSearchingStatusMsg, statusStr);
  131.     strcat(statusStr, groupName);
  132.     err = DisplayStatusMessage(statusStr);
  133.     if (err != noErr) return err;
  134.     
  135.     err = GetGroupArticleRange(theGroup, &groupExists);
  136.     if (err != noErr) return err;
  137.     if (!groupExists) return noErr;
  138.     
  139.     theGroup->numUnread = 0;
  140.     err = SearchHeaders(groupName, header, theGroup->firstMess,
  141.         theGroup->lastMess, pattern, &headers, &numHeaders);
  142.     if (err != noErr) return err;
  143.     if (headers == nil) return noErr;
  144.  
  145.     MyHLock(headers);
  146.     pHeaderEnd = *headers + numHeaders;
  147.     firstUnread = 0;
  148.     for (pHeader = *headers; pHeader < pHeaderEnd; pHeader++) {
  149.         number = pHeader->number;
  150.         if (firstUnread == 0) {
  151.             firstUnread = lastUnread = number;
  152.         } else if (pHeader->number == lastUnread+1) {
  153.             lastUnread = number;
  154.         } else {
  155.             err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
  156.             if (err != noErr) goto exit;
  157.             firstUnread = lastUnread = number;
  158.         }
  159.     }
  160.     if (firstUnread != 0) {
  161.         err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
  162.         if (err != noErr) goto exit;
  163.     }
  164.     MyDisposeHandle(headers);
  165.     return noErr;
  166.     
  167. exit:
  168.  
  169.     MyDisposeHandle(headers);
  170.     return err;
  171. }
  172.  
  173.  
  174.  
  175. /*----------------------------------------------------------------------------
  176.     SearchGroups
  177.     
  178.     Search groups on the server and build a new group array containing
  179.     the matching groups and article lists.
  180.     
  181.     Entry:    wind = pointer to group, subject, or article window.
  182.             header = header name.
  183.             pattern = search string.
  184.     
  185.     Exit:    function result = error code.
  186.             *newGroupArray = array of matching group records.
  187.             *newNumGroups = number of matching group records.
  188. ----------------------------------------------------------------------------*/
  189.  
  190. static OSErr SearchGroups (WindowPtr wind, char *header, char *pattern,
  191.     TGroup ***newGroupArray, long *newNumGroups)
  192. {
  193.     TWindow **info;
  194.     TWindowKind kind;
  195.     BigListRef groupList;
  196.     long index, item;
  197.     TGroup **groupArray;
  198.     long **groupNameOffsets;
  199.     Boolean userGroupList;
  200.     TGroup theGroup; 
  201.     TGroup **theNewGroupArray = nil;
  202.     long theNewNumGroups = 0;
  203.     long numAllocated;
  204.     OSErr err = noErr;
  205.     Boolean done = false;
  206.     Boolean firstTime = true;
  207.     Handle newsgroups = nil;
  208.     long i, j, nameOffset, len;
  209.     CStr255 groupName;
  210.     char c;
  211.  
  212.     info = (TWindow**)GetWRefCon(wind);
  213.     kind = (**info).kind;
  214.     
  215.     err = MyNewHandle(100*sizeof(TGroup), &theNewGroupArray);
  216.     if (err != noErr) goto exit;
  217.     numAllocated = 100;
  218.     
  219.     switch (kind) {
  220.         case kGroup:
  221.             userGroupList = (**info).groupKind == kUserGroup;
  222.             groupList = (**info).groupList;
  223.             groupArray = (**info).groupArray;
  224.             groupNameOffsets = (**info).groupNameOffsets;
  225.             item = 0;
  226.             break;
  227.         case kSubject:
  228.             break;
  229.         case kArticle:
  230.             err = FindHeaderHandle((**info).fullText, "Newsgroups", &newsgroups);
  231.             if (err != noErr) goto exit;
  232.             len = MyGetHandleSize(newsgroups);
  233.             i = 0;
  234.             break;
  235.     }
  236.     
  237.     while (!done) {
  238.         switch (kind) {
  239.             case kGroup:
  240.                 item = BigLGetNextSelectedItem(groupList, item);
  241.                 done = item < 0;
  242.                 if (done) break;
  243.                 index = BigLGetData(groupList, item);
  244.                 if (userGroupList) {
  245.                     theGroup.nameOffset = (*groupArray)[index].nameOffset;
  246.                 } else {
  247.                     theGroup.nameOffset = (*groupNameOffsets)[index];
  248.                 }
  249.                 item++;
  250.                 break;
  251.             case kSubject:
  252.                 done = !firstTime;
  253.                 if (done) break;
  254.                 firstTime = false;
  255.                 theGroup.nameOffset = (**info).groupNameOffset;
  256.                 break;
  257.             case kArticle:
  258.                 while (true) {
  259.                     while (i < len && !isalnum((*newsgroups)[i])) i++;
  260.                     done = i >= len;
  261.                     if (done) break;
  262.                     j = 0;
  263.                     while (i < len && j < 256) {
  264.                         c = (*newsgroups)[i];
  265.                         if (isalnum(c) || c == '.') {
  266.                             groupName[j++] = c;
  267.                             i++;
  268.                         } else {
  269.                             break;
  270.                         }
  271.                     }
  272.                     done = j >= 256;
  273.                     if (done) break;
  274.                     groupName[j] = 0;
  275.                     nameOffset = FindGroupOffset(groupName);
  276.                     if (nameOffset != -1) {
  277.                         theGroup.nameOffset = nameOffset;
  278.                         break;
  279.                     }
  280.                 }
  281.                 break;
  282.         }
  283.         if (done) break;
  284.         theGroup.firstMess = theGroup.lastMess = theGroup.numUnread = 0;
  285.         theGroup.unread = nil;
  286.         err = SearchOneGroup(header, pattern, &theGroup);
  287.         if (err != noErr) goto exit;
  288.         if (theGroup.numUnread != 0) {
  289.             if (theNewNumGroups >= numAllocated) {
  290.                 numAllocated += 100;
  291.                 err = MySetHandleSize(theNewGroupArray, numAllocated*sizeof(TGroup));
  292.                 if (err != noErr) goto exit;
  293.             }
  294.             (*theNewGroupArray)[theNewNumGroups] = theGroup;
  295.             theNewNumGroups++;
  296.         }
  297.     }
  298.     
  299.     MySetHandleSize(theNewGroupArray, theNewNumGroups*sizeof(TGroup));
  300.     *newGroupArray = theNewGroupArray;
  301.     *newNumGroups = theNewNumGroups;
  302.     MyDisposeHandle(newsgroups);
  303.     return noErr;
  304.     
  305. exit:
  306.  
  307.     DisposeGroupArray(theNewGroupArray, theNewNumGroups);
  308.     MyDisposeHandle(newsgroups);
  309.     return err;
  310. }
  311.  
  312.  
  313.  
  314. /*----------------------------------------------------------------------------
  315.     DoSearch
  316.     
  317.     Handle the "Search" command.
  318.     
  319.     Entry:    wind = pointer to group, subject, or article window.
  320.     
  321.     Exit:    function result = error code.
  322. ----------------------------------------------------------------------------*/
  323.  
  324. OSErr DoSearch (WindowPtr wind)
  325. {
  326.     CStr255    header, pattern;
  327.     TGroup **groupArray = nil;
  328.     long numGroups;
  329.     WindowPtr newWind;
  330.     TWindow **info;
  331.     Str255 title;
  332.     OSErr err = noErr;
  333.     TSavedWindPos pos;
  334.     
  335.     /* Present the search dialog. */
  336.     
  337.     err = DoSearchDialog(header, pattern);
  338.     if (err != noErr) return err;
  339.     
  340.     /* Search the groups. */
  341.     
  342.     err = SearchGroups(wind, header, pattern, &groupArray, &numGroups);
  343.     if (err != noErr) return err;
  344.     if (numGroups == 0) {
  345.         NoteMessageNumber(kStrNoMatches);
  346.         MyDisposeHandle(groupArray);
  347.         return noErr;
  348.     }
  349.     
  350.     /* Create the window. */
  351.     
  352.     GetPString(kStrSearchWindowTitle, title);
  353.     pos.valid = false;
  354.     err = MakeUserGroupWindow(title, groupArray, numGroups, &pos, &newWind);
  355.     if (err != noErr) return err;
  356.     info = (TWindow**)GetWRefCon(newWind);
  357.     (**info).okToCloseIfChanged = true;
  358.     return noErr;
  359. }
  360.